home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_200 / 264_01 / ls.c < prev    next >
Text File  |  1980-01-01  |  16KB  |  661 lines

  1. /*
  2.  * ls - list contents of directory
  3.  *
  4.  * Usage: ls [-alrstxzAR1] [path...]
  5.  *
  6.  * Flags:
  7.  * -a   list all files, including hidden and system files, ".", and ".."
  8.  * -l   long listing form (extra information)
  9.  * -r   reverse order of sorting
  10.  * -s   display size of each file in kilobytes, and total for each directory
  11.  * -t   sort by time/date (latest first)
  12.  * -x    sort by extension
  13.  * -z    sort by size
  14.  * -A   list all files except "." and ".."
  15.  * -R    list subdirectories recursively
  16.  * -1    display 1 entry per line of short form
  17.  *
  18.  * This program is in the public domain.
  19.  * David MacKenzie
  20.  * 6522 Elgin Lane
  21.  * Bethesda, MD 20817
  22.  *
  23.  * Latest revision: 05/07/88
  24.  */
  25.  
  26. #include <ctype.h>
  27. #ifndef tolower
  28. #define tolower(s) _tolower(s)
  29. #endif
  30. #include "getdir.h"
  31.  
  32. /* Arbitrary internal limit on filename path length. */
  33. #define MAXPATH 125
  34.  
  35. /* For dynamically allocating space to store contents of each directory. */
  36. #define ENTRIES_INCR 25
  37.  
  38. /*
  39.  * isdir(e)
  40.  *     struct sablk *e;
  41.  */
  42. #define isdir(e) ((e)->sa_attrib & FA_DIREC)
  43.  
  44. /*
  45.  * kbytes(b)
  46.  *     int b;
  47.  * Macro to return the number of kilobytes (rounded up) taken by a given
  48.  * number of bytes.
  49.  */
  50. #define kbytes(b) ((b) / 1024 + ((b) % 1024 != 0))
  51.  
  52. /* Shortened format for array in memory, to save space. */
  53. struct sablk {
  54.     char    sa_attrib;
  55.     unsigned sa_ftime;
  56.     unsigned sa_fdate;
  57.     unsigned long sa_fsize;
  58.     char   *sa_fname;
  59. };
  60.  
  61. /*
  62.  * Linked list of names of directories to list recursively (automatically
  63.  * initialized to NULL).
  64.  */
  65. struct dirs {
  66.     char   *dirs_name;
  67.     struct dirs *dirs_next;
  68. }      *dirlist;
  69.  
  70. char    dirbuf[512];        /* Buffer for disk input. */
  71.  
  72. char   *monthtab[] = {
  73.     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  74.     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  75. };
  76.  
  77. int     aflag = 0;        /* Display . and .. */
  78. int     lflag = 0;        /* Long listing form. */
  79. int     rflag = 0;        /* Reverse order of sort. */
  80. int     sflag = 0;        /* Display file sizes. */
  81. int     tflag = 0;        /* Sort by time/date. */
  82. int     xflag = 0;        /* Sort by extension. */
  83. int     zflag = 0;        /* Sort by size. */
  84. int     Aflag = 0;        /* Display hidden and system files. */
  85. int     Rflag = 0;        /* Display subdirectories recursively. */
  86. int     oneflag = 0;        /* One entry per line in short listing. */
  87.  
  88. int     listingargs;        /* Boolean for sort comparison function. */
  89.  
  90. char   *
  91. Malloc(), *Realloc();
  92.  
  93. main(argc, argv)
  94.     int     argc;
  95.     char  **argv;
  96. {
  97.     void    listfiles(), listdir();
  98.     int     entcmp();
  99.     struct sablk *initarray(), *saveent();
  100.     char   *basename(), *index();
  101.     struct dirs *dirp;
  102.     struct ffblk *dir = (struct ffblk *) dirbuf;
  103.     struct sablk *save_array;    /* Array of dir entries for sorting. */
  104.     int     last_entry = -1;    /* Last valid entry of save_array. */
  105.     int     last_normal;    /* Last non-directory entry. */
  106.     char    pathbuf[MAXPATH];    /* Path of globbed arguments. */
  107.     char   *tail;        /* Tail component of pathbuf. */
  108.     int     optind;        /* Loop index. */
  109.  
  110.     for (optind = 1; optind < argc && *argv[optind] == '-'; ++optind) {
  111.     while (*++argv[optind]) {
  112.         switch (*argv[optind]) {
  113.         case 'a':
  114.         aflag = Aflag = 1;
  115.         break;
  116.         case 'l':
  117.         lflag = 1;
  118.         break;
  119.         case 'r':
  120.         rflag = 1;
  121.         break;
  122.         case 's':
  123.         sflag = 1;
  124.         break;
  125.         case 't':
  126.         tflag = 1;
  127.         break;
  128.         case 'x':
  129.         xflag = 1;
  130.         break;
  131.         case 'z':
  132.         zflag = 1;
  133.         break;
  134.         case 'A':
  135.         Aflag = 1;
  136.         break;
  137.         case 'R':
  138.         Rflag = 1;
  139.         break;
  140.         case '1':
  141.         oneflag = 1;
  142.         break;
  143.         default:
  144.         printf("Usage: ls [-alrstxzAR1] [path...]\n");
  145.         exit(1);
  146.         break;
  147.         }
  148.     }
  149.     }
  150.  
  151.     setdta(dirbuf);        /* Set disk transfer area. */
  152.  
  153.     if (optind == argc)
  154.     argv[argc++] = ".";
  155.  
  156.     save_array = initarray();
  157.     for (; optind < argc; ++optind) {
  158.     /*
  159.      * Normally, when we say getfirst("dir") or getnext("dir"), where
  160.      * "dir" is the name of a directory, they return "dir" back to us
  161.      * along with its attributes, by which we discover that it has the
  162.      * FA_DIREC attribute.  However, for certain pseudo-directories
  163.      * (which are real, regular directories on Unix), if we say, for
  164.      * instance, getfirst("\"), we get nothing back - no matches. To work
  165.      * around that problem, we have this special test for the problem
  166.      * pseudo-directories. 
  167.      */
  168.     if (isroot(argv[optind]) || isdot(argv[optind])) {
  169.         (void) strcpy(dir->ff_fname, argv[optind]);
  170.         dir->ff_attrib = FA_DIREC;
  171.         dir->ff_ftime = dir->ff_fdate = 0;
  172.         dir->ff_fsize = 0L;
  173.         save_array = saveent(save_array, dir, ++last_entry);
  174.     } else if (!getfirst(argv[optind], FA_ALL)) {
  175.         /*
  176.          * Copy any leading drive and/or path into pathbuf because the
  177.          * MS-DOS globbing routines return only the base of the
  178.          * filenames. 
  179.          */
  180.         (void) strcpy(pathbuf, argv[optind]);
  181.         tail = basename(pathbuf);
  182.  
  183.         do {
  184.         if (dir->ff_fname[0] != '.' &&
  185.             (Aflag ||
  186.             (dir->ff_attrib & (FA_HIDDEN | FA_SYSTEM)) == 0)) {
  187.             (void) strcpy(tail, dir->ff_fname);
  188.             (void) strcpy(dir->ff_fname, pathbuf);
  189.             save_array = saveent(save_array, dir, ++last_entry);
  190.         }
  191.         } while (!getnext(argv[optind], FA_ALL));
  192.     } else if (index(argv[optind], '*') || index(argv[optind], '?')) {
  193.         printf("No match.\n");
  194.         exit(1);
  195.     }
  196.     }
  197.     listingargs = 1;
  198.     qsort((char *) save_array, last_entry + 1, sizeof(struct sablk),
  199.     entcmp);
  200.  
  201.     /* Find last non-directory entry. */
  202.     for (last_normal = last_entry;
  203.     last_normal >= 0 && isdir(&save_array[last_normal]);
  204.     --last_normal);
  205.  
  206.     /* First the regular listing... */
  207.     listfiles(save_array, last_normal);
  208.  
  209.     /* ...then the directories. */
  210.     if (last_normal < last_entry) {
  211.     if (last_normal++ >= 0)
  212.         putchar('\n');
  213.     for (;;) {
  214.         listdir(save_array[last_normal].sa_fname, last_entry > 0);
  215.         /*
  216.          * We avoid a lot of memory waste by not using recursive function
  217.          * calls to implement recursive listing. 
  218.          */
  219.         while (dirlist) {
  220.         /* Chop head of list off and show it. */
  221.         dirp = dirlist;
  222.         dirlist = dirp->dirs_next;
  223.         putchar('\n');
  224.         /* This call might add a new head to the list! */
  225.         listdir(dirp->dirs_name, 1);
  226.         free(dirp->dirs_name);
  227.         free((char *) dirp);
  228.         }
  229.         if (++last_normal > last_entry)
  230.         break;
  231.         putchar('\n');
  232.     }
  233.     }
  234.     exit(0);
  235. }
  236.  
  237. /*
  238.  * Display contents of one directory.
  239.  */
  240. void
  241. listdir(name, label)
  242.     char   *name;        /* Name of directory to display. */
  243.     int     label;        /* Diplay the directory's name:? */
  244. {
  245.     int     entcmp();
  246.     void    listfiles(), addsubdir();
  247.     char   *concat(), *savestr();
  248.     struct sablk *save_array;
  249.     int     last_entry;
  250.     int     total_kbytes;
  251.     int     i;            /* Loop index. */
  252.  
  253.     total_kbytes = getdir(name, &save_array, &last_entry);
  254.  
  255.     if (label)
  256.     printf("%s:\n", name);
  257.     if (lflag || sflag)
  258.     printf("total %d\n", total_kbytes);
  259.     if (last_entry == -1)
  260.     return;
  261.  
  262.     listingargs = 0;
  263.     qsort((char *) save_array, last_entry + 1, sizeof(struct sablk),
  264.     entcmp);
  265.     listfiles(save_array, last_entry);
  266.  
  267.     if (Rflag)
  268.     for (i = last_entry; i >= 0; --i)
  269.         if (isdir(&save_array[i]) && save_array[i].sa_fname[0] != '.')
  270.         /* Add entry to list of subdirectories. */
  271.         addsubdir(concat(name, save_array[i].sa_fname));
  272.     for (i = 0; i <= last_entry; ++i)
  273.     free(save_array[i].sa_fname);
  274.     free(save_array);
  275. }
  276.  
  277. /*
  278.  * Read contents of directory into array and return size in kilobytes.
  279.  */
  280. getdir(name, psave_array, plast_entry)
  281.     char   *name;
  282.     struct sablk **psave_array;
  283.     int    *plast_entry;
  284. {
  285.     char   *concat();
  286.     struct sablk *initarray(), *saveent();
  287.     struct ffblk *dir = (struct ffblk *) dirbuf;
  288.     struct sablk *save_array;    /* Array of dir entries for sorting. */
  289.     int     last_entry = -1;    /* Last valid entry of save_array. */
  290.     int     total_kbytes = 0;    /* Ignored if sflag and lflag not given. */
  291.     char   *nametmp;
  292.     int     i;            /* Temporary. */
  293.  
  294.     save_array = initarray();
  295.     na